﻿using System;
using System.Collections.Generic;
using Processing001.Library.AbstractClasses;
using Processing001.Library.Interfaces;

namespace Processing001.Library
{
    public class FurieWrapper : AlgorithmWrapperAbstract<ComplexNumber>
    {
        private static int size = 8;

        public FurieWrapper()
        {
        }

        public FurieWrapper(int size)
        {
            FurieWrapper.size = size;
        }

        public class FurieRight : ITask<ComplexNumber>
        {
            public List<ComplexNumber> Generate(List<ComplexNumber> values)
            {
                ComplexNumber[] result = new ComplexNumber[values.Count];
                if (values.Count == 1)
                {
                    return values;
                }
                List<ComplexNumber> values1 = new List<ComplexNumber>();
                List<ComplexNumber> values2 = new List<ComplexNumber>();
                FurieWrapper.SplitVector(values, ref values1, ref values2);

                values1 = Generate(values1);
                values2 = Generate(values2);

                ComplexNumber number = new ComplexNumber(Math.Cos(2 * Math.PI / values.Count), Math.Sin(2 * Math.PI / values.Count));
                ComplexNumber w = new ComplexNumber(1, 0);

                for(int j = 0; j < values.Count / 2; j++)
                {
                    result[j] = values1[j].Add(values2[j].Mul(w));
                    result[j + values.Count / 2] = values1[j].Sub(values2[j].Mul(w));
                    w = number.Mul(w);
                }
                return new List<ComplexNumber>(result);
            }
        }

        public class FurieLeft : ITask<ComplexNumber>
        {
            public List<ComplexNumber> Generate(List<ComplexNumber> values)
            {
                ComplexNumber[] result = new ComplexNumber[values.Count];
                if (values.Count == 1)
                {
                    return values;
                }
                List<ComplexNumber> values1 = new List<ComplexNumber>();
                List<ComplexNumber> values2 = new List<ComplexNumber>();
                FurieWrapper.SplitVector(values, ref values1, ref values2);

                values1 = Generate(values1);
                values2 = Generate(values2);

                ComplexNumber number = new ComplexNumber(Math.Cos(2 * Math.PI / values.Count), -Math.Sin(2 * Math.PI / values.Count));
                ComplexNumber w = new ComplexNumber(1, 0);

                for (int j = 0; j < values.Count / 2; j++)
                {
                    result[j] = values1[j].Add(values2[j].Mul(w));
                    result[j + values.Count / 2] = values1[j].Sub(values2[j].Mul(w));
                    w = number.Mul(w);
                }
                return new List<ComplexNumber>(result);
            }
        }

        private static void SplitVector(List<ComplexNumber> source, ref List<ComplexNumber> values1, ref List<ComplexNumber> values2)
        {
            values1.Clear();
            values2.Clear();
            for(int i = 0; i < source.Count; i++)
            {
                if (i % 2 == 0)
                {
                    values1.Add(source[i]);
                } 
                else
                {
                    values2.Add(source[i]);
                }
            }
        }

        public override List<ComplexNumber> GenerateByWay(Way way, List<ComplexNumber> values)
        {
            List<ComplexNumber> numbers = new List<ComplexNumber>();
            switch(way)
            {
                case Way.Right:
                    FurieRight right = new FurieRight();
                    numbers = right.Generate(values);
                    break;
                case Way.Left:
                    FurieLeft left = new FurieLeft();
                    foreach (ComplexNumber value in values)
                    {
                        value.Real /= size;
                        value.Image /= size;
                    }
                    numbers = left.Generate(values);
                    break;
                case Way.None:
                    break;
            }
            return numbers;
        }
    }
}
